﻿//  --------------------------------
//  Copyright (c) 2010 Michael Schuler, Sascha Burger. All rights reserved.
//  This source code is made available under the terms of the Microsoft Public License (Ms-PL)
//  http://p2pmessaging.codeplex.com/license
//  ---------------------------------
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using p2pMessaging.Interfaces.Contact;

namespace p2pMessaging.BL.Model
{
	public class AddressBook : IEnumerator<IContact>, ICollection<IContact>
	{
		private readonly IteratorItem myFirstItem = new IteratorItem();
		private IteratorItem myCurrentItem;
		
		public AddressBook()
		{
			myCurrentItem = myFirstItem;
		}
		
		public AddressBook(IContact[] contacts) : this()
		{
			foreach (IContact contact in contacts)
			{
				Add(contact);
			}
		}

		public AddressBook(IList<IContact> contacts) : this()
		{
			foreach (IContact contact in contacts)
			{
				Add(contact);
			}
		}

		/// <summary>
		/// Gets the element in the collection at the current position of the enumerator.
		/// </summary>
		/// <returns>
		/// The element in the collection at the current position of the enumerator.
		/// </returns>
		public IContact Current
		{
			get
			{
				return myCurrentItem.Current;
			}
		}

		/// <summary>
		/// Gets the current element in the collection.
		/// </summary>
		/// <returns>
		/// The current element in the collection.
		/// </returns>
		/// <exception cref="T:System.InvalidOperationException">The enumerator is positioned before the first element of the collection or after the last element.
		/// </exception><filterpriority>2</filterpriority>
		object IEnumerator.Current
		{
			get { return Current; }
		}

		/// <summary>
		/// Gets the number of elements contained in the <see cref="T:System.Collections.Generic.ICollection`1"/>.
		/// </summary>
		/// <returns>
		/// The number of elements contained in the <see cref="T:System.Collections.Generic.ICollection`1"/>.
		/// </returns>
		public int Count
		{
			get
			{
				int count = 0;
				IteratorItem item = myFirstItem.Next;
				while (item != null)
				{
					count += 1;
					item = item.Next;
				}

				return count;
			}
		}

		/// <summary>
		/// Gets a value indicating whether the <see cref="T:System.Collections.Generic.ICollection`1"/> is read-only.
		/// </summary>
		/// <returns>
		/// true if the <see cref="T:System.Collections.Generic.ICollection`1"/> is read-only; otherwise, false.
		/// </returns>
		public bool IsReadOnly
		{
			get { return false; }
		}

		/// <summary>
		/// Advances the enumerator to the next element of the collection.
		/// </summary>
		/// <returns>
		/// true if the enumerator was successfully advanced to the next element; false if the enumerator has passed the end of the collection.
		/// </returns>
		/// <exception cref="T:System.InvalidOperationException">The collection was modified after the enumerator was created. 
		/// </exception><filterpriority>2</filterpriority>
		public bool MoveNext()
		{
			if (myCurrentItem != null && myCurrentItem.Next != null)
			{
				myCurrentItem = myCurrentItem.Next;
				return true;
			}

			return false;
		}

		/// <summary>
		/// Sets the enumerator to its initial position, which is before the first element in the collection.
		/// </summary>
		/// <exception cref="T:System.InvalidOperationException">The collection was modified after the enumerator was created. 
		/// </exception><filterpriority>2</filterpriority>
		public void Reset()
		{
			myCurrentItem = myFirstItem;
		}

		/// <summary>
		/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
		/// </summary>
		/// <filterpriority>2</filterpriority>
		public void Dispose()
		{
			Clear();
		}

		/// <summary>
		/// Returns an enumerator that iterates through the collection.
		/// </summary>
		/// <returns>
		/// A <see cref="T:System.Collections.Generic.IEnumerator`1"/> that can be used to iterate through the collection.
		/// </returns>
		/// <filterpriority>1</filterpriority>
		public IEnumerator<IContact> GetEnumerator()
		{
			return this;
		}

		/// <summary>
		/// Returns an enumerator that iterates through a collection.
		/// </summary>
		/// <returns>
		/// An <see cref="T:System.Collections.IEnumerator"/> object that can be used to iterate through the collection.
		/// </returns>
		/// <filterpriority>2</filterpriority>
		IEnumerator IEnumerable.GetEnumerator()
		{
			return GetEnumerator();
		}

		/// <summary>
		/// Adds an item to the <see cref="T:System.Collections.Generic.ICollection`1"/>.
		/// </summary>
		/// <param name="item">The object to add to the <see cref="T:System.Collections.Generic.ICollection`1"/>.</param>
		/// <exception cref="T:System.NotSupportedException">The <see cref="T:System.Collections.Generic.ICollection`1"/> is read-only.
		/// </exception>
		public void Add(IContact item)
		{
			if (Contains(item))
			{
				return;
			}
			
			IteratorItem newItem = new IteratorItem { Current = item };
			IteratorItem iteratorItem = findLast();
			
			if (iteratorItem.Current != null)
			{
				iteratorItem.Next = newItem;
				newItem.Previous = iteratorItem;
			}
			else
			{
				myFirstItem.Next = newItem;
				newItem.Previous = myFirstItem;
			}

			foreach (IContact child in item.Children)
			{
				Add(child);
			}
		}

		/// <summary>
		/// Removes all items from the <see cref="T:System.Collections.Generic.ICollection`1"/>.
		/// </summary>
		/// <exception cref="T:System.NotSupportedException">The <see cref="T:System.Collections.Generic.ICollection`1"/> is read-only.</exception>
		public void Clear()
		{
			myCurrentItem = null;
			myFirstItem.Next = null;
		}

		/// <summary>
		/// Determines whether the <see cref="T:System.Collections.Generic.ICollection`1"/> contains a specific value.
		/// </summary>
		/// <returns>
		/// true if <paramref name="item"/> is found in the <see cref="T:System.Collections.Generic.ICollection`1"/>; otherwise, false.
		/// </returns>
		/// <param name="item">The object to locate in the <see cref="T:System.Collections.Generic.ICollection`1"/>.
		/// </param>
		public bool Contains(IContact item)
		{
			return find(item) != null;
		}

		/// <summary>
		/// Copies the elements of the <see cref="T:System.Collections.Generic.ICollection`1"/> to an <see cref="T:System.Array"/>, starting at a particular <see cref="T:System.Array"/> index.
		/// </summary>
		/// <param name="array">The one-dimensional <see cref="T:System.Array"/> that is the destination of the elements copied from <see cref="T:System.Collections.Generic.ICollection`1"/>. The <see cref="T:System.Array"/> must have zero-based indexing.
		/// </param><param name="arrayIndex">The zero-based index in <paramref name="array"/> at which copying begins.
		/// </param><exception cref="T:System.ArgumentNullException"><paramref name="array"/> is null.
		/// </exception><exception cref="T:System.ArgumentOutOfRangeException"><paramref name="arrayIndex"/> is less than 0.
		/// </exception><exception cref="T:System.ArgumentException"><paramref name="array"/> is multidimensional.
		/// -or-
		/// <paramref name="arrayIndex"/> is equal to or greater than the length of <paramref name="array"/>.
		/// -or-
		/// The number of elements in the source <see cref="T:System.Collections.Generic.ICollection`1"/> is greater than the available space from <paramref name="arrayIndex"/> to the end of the destination <paramref name="array"/>.
		/// </exception>
		public void CopyTo(IContact[] array, int arrayIndex)
		{
			throw new NotImplementedException();
		}

		/// <summary>
		/// Removes the first occurrence of a specific object from the <see cref="T:System.Collections.Generic.ICollection`1"/>.
		/// </summary>
		/// <returns>
		/// true if <paramref name="item"/> was successfully removed from the <see cref="T:System.Collections.Generic.ICollection`1"/>; otherwise, false. This method also returns false if <paramref name="item"/> is not found in the original <see cref="T:System.Collections.Generic.ICollection`1"/>.
		/// </returns>
		/// <param name="item">The object to remove from the <see cref="T:System.Collections.Generic.ICollection`1"/>.
		/// </param><exception cref="T:System.NotSupportedException">The <see cref="T:System.Collections.Generic.ICollection`1"/> is read-only.
		/// </exception>
		public bool Remove(IContact item)
		{
			IteratorItem iteratorItem = find(item);
			if (iteratorItem != null)
			{
				iteratorItem.Previous.Next = iteratorItem.Next;
				return true;
			}

			return false;
		}
		
		/// <summary>
		/// Gets a list with all matching Contacts from this address book. Tested fields are <see cref="IContact.City"/>,
		/// <see cref="IContact.DisplayName"/>, <see cref="IContact.EmailAddress"/>, <see cref="IContact.FirstName"/>,
		/// <see cref="IContact.LastName"/>, <see cref="IContact.IpAddress"/>, <see cref="IContact.Street"/>,
		/// <see cref="IContact.ZipCode"/>.
		/// </summary>
		/// <param name="pattern">regex pattern</param>
		/// <returns>list of all matching contacts.</returns>
		public IList<IContact> Find(string pattern)
		{
			List<IContact> result = new List<IContact>();
			Regex regex = new Regex(pattern, RegexOptions.IgnoreCase | RegexOptions.Singleline | RegexOptions.Compiled);

			IteratorItem item = myFirstItem;
			while (item != null)
			{
				if (item.Current != null &&
					(regex.IsMatch(item.Current.City) ||
					regex.IsMatch(item.Current.DisplayName) ||
					regex.IsMatch(item.Current.EmailAddress) ||
					regex.IsMatch(item.Current.FirstName) ||
					regex.IsMatch(item.Current.LastName) ||
					regex.IsMatch(item.Current.IpAddress) ||
					regex.IsMatch(item.Current.Street) ||
					regex.IsMatch(item.Current.ZipCode)))
				{
					result.Add(item.Current);
				}
				
				item = item.Next;
			}

			return result.AsReadOnly();
		}
		
		private IteratorItem find(IContact item)
		{
			IteratorItem iteratorItem = myFirstItem.Next;
			while (iteratorItem != null)
			{
				if (iteratorItem.Current.Equals(item))
				{
					return iteratorItem;
				}
				
				iteratorItem = iteratorItem.Next;
			}
			
			return null;
		}
		
		private IteratorItem findLast()
		{
			IteratorItem last = myFirstItem;
			while (last != null && last.Next != null)
			{
				last = last.Next;
			}
			
			return last;
		}
		
		private class IteratorItem
		{
			public IContact Current { get; set; }
			
			public IteratorItem Next { get; set; }
			
			public IteratorItem Previous { get; set; }
		}
	}
}
